﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Dynamic;
using System.Linq.Expressions;
using System.Reflection;

namespace TurboTemplate
{
    // http://loosexaml.wordpress.com/2011/01/01/wcf-serialization-of-dlr-dynamic-types/
    [DataContract()]
    public class DynamicDictionary : IDynamicMetaObjectProvider
    {
        [DataMember()]
        private IDictionary<object, object> dictionary = new Dictionary<object, object>();

        public int Count
        {
            get
            {
                return dictionary.Count;
            }
        }

        //public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result)
        //{
        //    string name = binder.Name.ToLower();
        //    if (!dictionary.TryGetValue(name, out result))
        //        result = null;
        //    return true;
        //}

        //public override bool TrySetMember(System.Dynamic.SetMemberBinder binder, object value)
        //{
        //    dictionary[binder.Name.ToLower()] = value;
        //    return true;
        //}

        public object SetValue(string name, object value)
        {
            dictionary[name.ToLower()] = value;
            return value;
        }

        public object GetValue(string name)
        {
            object value;
            if (!dictionary.TryGetValue(name.ToLower(), out value))
            {
                value = null;
            }
            return value;
        }

        //public override IEnumerable<string> GetDynamicMemberNames()
        //{
        //    return dictionary.Keys.Cast<string>();
        //}

        DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression parameter)
        {
            return new SerializableDynamicMetaObject(parameter, BindingRestrictions.GetInstanceRestriction(parameter, this), this);
        }

        internal IEnumerable<string> GetDynamicMemberNames()
        {
            return dictionary.Keys.Cast<string>();
        }

    }

    public class SerializableDynamicMetaObject : DynamicMetaObject
    {
        Type objType;

        public SerializableDynamicMetaObject(Expression expression, BindingRestrictions restrictions, object value)
            : base(expression, restrictions, value)
        {
            objType = value.GetType();
        }

        public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
        {
            var self = this.Expression;
            var dynObj = (DynamicDictionary)this.Value;
            var keyExpr = Expression.Constant(binder.Name);
            var getMethod = objType.GetMethod("GetValue", BindingFlags.NonPublic | BindingFlags.Instance);
            var target = Expression.Call(Expression.Convert(self, objType), getMethod, keyExpr);
            return new DynamicMetaObject(target, BindingRestrictions.GetTypeRestriction(self, objType));
        }

        public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
        {
            var self = this.Expression;
            var keyExpr = Expression.Constant(binder.Name);
            var valueExpr = Expression.Convert(value.Expression, typeof(object));
            var setMethod = objType.GetMethod("SetValue", BindingFlags.NonPublic | BindingFlags.Instance);
            var target = Expression.Call(Expression.Convert(self, objType), setMethod, keyExpr, valueExpr);
            return new DynamicMetaObject(target, BindingRestrictions.GetTypeRestriction(self, objType));
        }

        public override IEnumerable<string> GetDynamicMemberNames()
        {
            var dynObj = (DynamicDictionary)this.Value;
            return dynObj.GetDynamicMemberNames();
        }
    }
}
